package de.hmm.hwip.deviceconfig;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;

import android.content.ContentResolver;
import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.text.format.Formatter;
import android.util.Log;

/** class to manage the network connections
 * it sets and gets network configurations
 * 
 * @author johannes mendel
 *
 */
public class DeviceConfiguration {

	private final static String TAG = "DeviceConfiguration - Messagehandler";
	
	private Context androidContext;
	
	WifiConfiguration wifiConf = null;
	WifiManager wifiManager;
	WifiInfo connectionInfo;
	String dns;
	String netmask;
	String gateway;
	String dhcp;
	
	/** constructor with android application context **/
	public DeviceConfiguration(Context context){
		this.androidContext = context;
	}
	
	/** gets the momentaneously ip adress and sets other values to network configuration **/
	public String getIp(){
    	
    	wifiConf = null;
    	wifiManager = (WifiManager)androidContext.getSystemService(Context.WIFI_SERVICE);
    	connectionInfo = wifiManager.getConnectionInfo();
    	
		int tempAddress = connectionInfo.getIpAddress();
		String ipAddress = Formatter.formatIpAddress(tempAddress);
		gateway = Formatter.formatIpAddress(wifiManager.getDhcpInfo().gateway);  
		netmask = Formatter.formatIpAddress(wifiManager.getDhcpInfo().netmask);
		dhcp = Formatter.formatIpAddress(wifiManager.getDhcpInfo().serverAddress);
		dns = Formatter.formatIpAddress( wifiManager.getDhcpInfo().dns1);
		
		return ipAddress;
	}
	
	/** sets the ip adress configuration for whole network
	 * @param adress - ip adress
	**/
	public String setIp(int adress){
    	String newAdress = null;
		String ipAddress = getIp();
    	List<WifiConfiguration> configuredNetworks = wifiManager.getConfiguredNetworks();        
    	for (WifiConfiguration conf : configuredNetworks){
    		if (conf.networkId == connectionInfo.getNetworkId()){
    	              wifiConf = conf;
    	              break;              
    		}
    	}
    	
    	try{
    		newAdress = trimIpAdress(ipAddress) + adress;
    		setIpAssignment("STATIC", wifiConf); //or "DHCP" for dynamic setting
    		setIpAddress(InetAddress.getByName(newAdress), 24, wifiConf);
    		setGateway(InetAddress.getByName(gateway), wifiConf);
    		setDNS(InetAddress.getByName(dns), wifiConf);
    		wifiManager.updateNetwork(wifiConf); //apply the setting
    	}catch(NoSuchFieldException e){
    		setIpAndroid2(newAdress);
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    	
    	return newAdress;
    }
	
	/** sets the ip assignment **/ 
    public static void setIpAssignment(String assign , WifiConfiguration wifiConf)
    	    throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException{
    	setEnumField(wifiConf, assign, "ipAssignment");     
    }

    /** sets only the ip adress **/
    public static void setIpAddress(InetAddress addr, int prefixLength, WifiConfiguration wifiConf)
    		throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException,
    		NoSuchMethodException, ClassNotFoundException, InstantiationException, InvocationTargetException{
    	Object linkProperties = getField(wifiConf, "linkProperties");
    	if(linkProperties == null)return;
    	Class laClass = Class.forName("android.net.LinkAddress");
    	Constructor laConstructor = laClass.getConstructor(new Class[]{InetAddress.class, int.class});
    	Object linkAddress = laConstructor.newInstance(addr, prefixLength);
    	
    	ArrayList mLinkAddresses = (ArrayList)getDeclaredField(linkProperties, "mLinkAddresses");
    	mLinkAddresses.clear();
    	mLinkAddresses.add(linkAddress);        
    }

    /** sets the gateway address **/
    public static void setGateway(InetAddress gateway, WifiConfiguration wifiConf)
    		throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException, 
    	    ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException{
    	Object linkProperties = getField(wifiConf, "linkProperties");
    	if(linkProperties == null)return;
    	Class routeInfoClass = Class.forName("android.net.RouteInfo");
    	Constructor routeInfoConstructor = routeInfoClass.getConstructor(new Class[]{InetAddress.class});
    	Object routeInfo = routeInfoConstructor.newInstance(gateway);
    	
    	ArrayList mRoutes = (ArrayList)getDeclaredField(linkProperties, "mRoutes");
    	mRoutes.clear();
    	mRoutes.add(routeInfo);
    }

    /** sets the dns **/
    public static void setDNS(InetAddress dns, WifiConfiguration wifiConf)
    		throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException{
    	Object linkProperties = getField(wifiConf, "linkProperties");
    	if(linkProperties == null)return;
    	
    	ArrayList<InetAddress> mDnses = (ArrayList<InetAddress>)getDeclaredField(linkProperties, "mDnses");
    	mDnses.clear(); //or add a new dns address , here I just want to replace DNS1
    	mDnses.add(dns); 
    }
    
    /** gets the different oktetts of ip address **/
    public static Object getField(Object obj, String name)
    		throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException{
    	Field f = obj.getClass().getField(name);
    	Object out = f.get(obj);
    	return out;
    }
    
    /** method to get the fields of values **/
    public static Object getDeclaredField(Object obj, String name)
    		throws SecurityException, NoSuchFieldException,
    		IllegalArgumentException, IllegalAccessException {
    	Field f = obj.getClass().getDeclaredField(name);
    	f.setAccessible(true);
    	Object out = f.get(obj);
    	return out;
    }  
    
    
    public static void setEnumField(Object obj, String value, String name)
    		throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException{
    	Field f = obj.getClass().getField(name);
    	f.set(obj, Enum.valueOf((Class<Enum>) f.getType(), value));
    }
    
    /** trims the ip address to the first 3 oktetts **/
    private String trimIpAdress(String ipAdress){
    	String ipArea = null;
    	try{
			String[] adressField = new String[4];
			adressField = ipAdress.split("\\.");
			ipArea = adressField[0] + "." + adressField[1] + "." + adressField[2] + ".";
		}catch(IndexOutOfBoundsException e){
			Log.e(TAG, "Ip-transformation failed: " + e.getMessage());
		}
    	return ipArea;
    }
    
    /** gets one requested block **/
    private int trimAdressToLastBlock(String ipAdress){
    	int lastBlock = 0;
    	try{
			String[] adressField = new String[4];
			adressField = ipAdress.split("\\.");
			lastBlock = Integer.parseInt(adressField[3]);
		}catch(IndexOutOfBoundsException e){
			Log.e(TAG, "Ip-transformation failed: " + e.getMessage());
		}catch(NumberFormatException e){
			Log.e(TAG, "Number format exception: " + e.getMessage());
		}
    	return lastBlock;
    }
    
    /** sets ip address on android 2.x **/
    private void setIpAndroid2(String newadress){
    	final ContentResolver cr = androidContext.getContentResolver();   
    	android.provider.Settings.System.putString(cr, android.provider.Settings.System.WIFI_STATIC_IP, newadress);
    	android.provider.Settings.System.putString(cr, android.provider.Settings.System.WIFI_USE_STATIC_IP, "1");     
    	android.provider.Settings.System.putString(androidContext.getContentResolver(), android.provider.Settings.System.WIFI_STATIC_NETMASK, netmask);
    	android.provider.Settings.System.putString(androidContext.getContentResolver(), android.provider.Settings.System.WIFI_STATIC_DNS1, dns);
    	android.provider.Settings.System.putString(androidContext.getContentResolver(), android.provider.Settings.System.WIFI_STATIC_GATEWAY, gateway);
    }
    
    /** getter for DNS **/
    public int getDNS(){
    	if(dns == null)
    		getIp();
    	
    	return trimAdressToLastBlock(dns);
    }
    
    /** getter for NetGateway **/
    public int getNetGateway(){
    	if(gateway == null)
    		getIp();
    	
    	return trimAdressToLastBlock(gateway);
    }
    
    /** getter for Dhcp **/
    public int getDhcp(){
    	if(dhcp == null)
    		getIp();
    	
    	return trimAdressToLastBlock(dhcp);
    }
}
